/*
Copyright (C) 2022-2024 Inspur Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/
package service

import (
	"encoding/json"
	"strconv"

	"github.com/go-xorm/xorm"
	"github.com/pkg/errors"

	"did-service/bean/entity"
	"did-service/bean/model"
	"did-service/bean/param"
	"did-service/common/constant"
	"did-service/common/db"
)

// 四表联查
type credentialIssuerCpt struct {
	entity.VerifiableCredential `xorm:"extends"`
	entity.CredentialCpt        `xorm:"extends"`
	entity.AuthorityIssuer      `xorm:"extends"`
	entity.RevokedCredential    `xorm:"extends"`
}

// 两表联查
type credentialCpt struct {
	entity.VerifiableCredential `xorm:"extends"`
	entity.CredentialCpt        `xorm:"extends"`
}

func (ds *DidService) QueryCredentialList(walletId string, para *param.ListCredentialParam, queryType bool) (*db.Page, error) {
	condition := ""
	args := make([]interface{}, 0)
	credentialList := make([]*model.CredentialInfo, 0)
	//判断用户是否为发证方
	isIssuer, validDid, err := verifyIsIssuer(ds.engine, walletId)
	if err != nil {
		return nil, err
	}

	pageSize := 10
	if para.PageSize != "" {
		if size, err := strconv.Atoi(para.PageSize); err == nil {
			pageSize = size
		}
	}
	pageNum := 1
	if para.PageNum != "" {
		if num, err := strconv.Atoi(para.PageNum); err == nil {
			pageNum = num
		}
	}
	page := db.NewPage(0, pageNum).SetPageSize(pageSize)

	//不包括已被吊销的凭证
	if queryType {
		combineSql("vc.status != ? ", &condition, constant.CredentialStatusInvalid, &args)
	} else {
		//已被吊销的凭证
		combineSql("vc.status = ? ", &condition, constant.CredentialStatusInvalid, &args)
	}
	//凭证名称
	if para.CredentialName != "" {
		s := " and cc.title like " + "'%" + para.CredentialName + "%' "
		combineSql(s, &condition, "", nil)
	}

	//普通用户增加筛选条件
	if !isIssuer {
		MultiDid, err := walletId2MultiDid(ds.engine, walletId)
		if err != nil {
			return nil, err
		}
		validIssuerDid, err := getValidIssuerDid(ds.engine, MultiDid, queryType)
		if err != nil {
			return nil, err
		}
		if len(validIssuerDid) == 0 {
			//用户无有效凭证或发证方所有did均被停用
			page.SetTotal(0)
			page.CreatePage(credentialList)
			return page, nil
		}
		//所有者Did
		combineSql(" and (owner_did = ? ", &condition, MultiDid[0], &args)
		if len(MultiDid) > 1 {
			for _, did := range MultiDid[1:] {
				combineSql(" or owner_did = ? ", &condition, did, &args)
			}
		}
		combineSql(")", &condition, "", nil)

		//发证方筛选条件
		combineSql(" and (issuer_did = ? ", &condition, validIssuerDid[0], &args)
		if len(validIssuerDid) > 1 {
			for _, did := range validIssuerDid[1:] {
				combineSql(" or issuer_did = ? ", &condition, did, &args)
			}
		}
		combineSql(")", &condition, "", nil)

		//发证方名称
		if para.IssuerName != "" {
			s := " and ai.name like " + "'%" + para.IssuerName + "%' "
			combineSql(s, &condition, "", nil)
		}
	} else {
		if len(validDid) == 0 {
			//发证方的所有did均停用，返回空结果
			page.SetTotal(0)
			page.CreatePage(credentialList)
			return page, nil
		}
		combineSql(" and (vc.issuer_did = ? ", &condition, validDid[0], &args)
		if len(validDid) > 1 {
			for _, did := range validDid[1:] {
				combineSql(" or vc.issuer_did = ? ", &condition, did, &args)
			}
		}
		combineSql(")", &condition, "", nil)
	}

	session := getJoinSession(ds.engine)
	var countEntity credentialIssuerCpt
	listNum, err := session.Where(condition, args...).Count(&countEntity)

	session = getJoinSession(ds.engine)
	list := make([]*credentialIssuerCpt, 0)
	err = session.Where(condition, args...).Limit(pageSize, (pageNum-1)*pageSize).Desc("vc.updated_time").Find(&list)
	if err != nil {
		return nil, errors.Errorf("query verifiable credential list error: %s", err)
	}

	for _, res := range list {
		credentialList = append(credentialList, &model.CredentialInfo{
			CredentialId:   res.VerifiableCredential.Id,
			CredentialName: res.CredentialCpt.Title,
			IssuerName:     issuerInfo(isIssuer, res.AuthorityIssuer.Name),
			IssuerDid:      issuerInfo(isIssuer, res.IssuerDid),
			CptId:          res.VerifiableCredential.CptId,
			Status:         res.VerifiableCredential.Status,
			RevokeDate:     RevokeDateOrReason(queryType, res.RevokeDate.Format(constant.TimeLayout)),
			RevokeReason:   RevokeDateOrReason(queryType, res.RevokeReason),
			CreatedTime:    res.VerifiableCredential.CreatedTime.Format(constant.TimeLayout),
			UpdatedTime:    res.VerifiableCredential.UpdatedTime.Format(constant.TimeLayout),
		})
	}
	page.SetTotal(listNum)
	page.CreatePage(credentialList)
	return page, nil
}

func (ds *DidService) QuerySimpleCredentialList(walletId string) ([]*model.CredentialInfo, error) {
	condition := ""
	args := make([]interface{}, 0)
	credentialList := make([]*model.CredentialInfo, 0)

	//不包括已被吊销的凭证
	combineSql("vc.status != ? ", &condition, constant.CredentialStatusInvalid, &args)

	MultiDid, err := walletId2MultiDid(ds.engine, walletId)
	if err != nil {
		return nil, err
	}
	validIssuerDid, err := getValidIssuerDid(ds.engine, MultiDid, true)
	if err != nil {
		return nil, err
	}
	if len(validIssuerDid) == 0 {
		//用户无有效凭证或发证方所有did均被停用
		return credentialList, nil
	}
	//所有者Did
	combineSql(" and (owner_did = ? ", &condition, MultiDid[0], &args)
	if len(MultiDid) > 1 {
		for _, did := range MultiDid[1:] {
			combineSql(" or owner_did = ? ", &condition, did, &args)
		}
	}
	combineSql(")", &condition, "", nil)

	//发证方筛选条件
	combineSql(" and (issuer_did = ? ", &condition, validIssuerDid[0], &args)
	if len(validIssuerDid) > 1 {
		for _, did := range validIssuerDid[1:] {
			combineSql(" or issuer_did = ? ", &condition, did, &args)
		}
	}
	combineSql(")", &condition, "", nil)

	session := getJoinSession2(ds.engine)
	list := make([]*credentialCpt, 0)
	err = session.Where(condition, args...).Desc("vc.updated_time").Find(&list)
	if err != nil {
		return nil, errors.Errorf("query simple verifiable credential list error: %s", err)
	}

	for _, res := range list {
		credentialList = append(credentialList, &model.CredentialInfo{
			CredentialId:   res.VerifiableCredential.Id,
			CredentialName: res.CredentialCpt.Title,
			CreatedTime:    res.VerifiableCredential.CreatedTime.Format(constant.TimeLayout),
			UpdatedTime:    res.VerifiableCredential.UpdatedTime.Format(constant.TimeLayout),
		})
	}
	return credentialList, nil
}

func (ds *DidService) QueryCredentialClaims(walletId, credentialId string) (*model.CredentialClaims, error) {
	err := verifyCredOwner(ds.engine, walletId, credentialId)
	if err != nil {
		return nil, err
	}
	credClaims, err := getCredClaims(ds.engine, credentialId)
	if err != nil {
		return nil, err
	}
	return &model.CredentialClaims{Claims: credClaims}, nil
}

func getCredClaims(engine *xorm.Engine, credId string) (claims string, err error) {
	//查询凭证表
	VerCred := new(entity.VerifiableCredential)
	ok, err := engine.Where("id = ?", credId).Get(VerCred)
	if err != nil {
		return "", err
	}
	if !ok {
		return "", errors.Errorf("the credential does not exist, credId :%s", credId)
	}

	credSubject := model.CredentialSubject{}
	err = json.Unmarshal([]byte(VerCred.CredentialSubject), &credSubject)
	if err != nil {
		return "", err
	}
	return credSubject.Claims, nil
}

// 查询凭证列表不返回 RevokeDate, RevokeDate
var RevokeDateOrReason = func(queryType bool, para string) string {
	if !queryType {
		return para
	}
	return ""
}

// 判断是否为发证方用户
func verifyIsIssuer(engine *xorm.Engine, walletId string) (ok bool, validDid []string, err error) {
	issuers := make([]*entity.AuthorityIssuer, 0)
	err = engine.Where("wallet_id = ?", walletId).Find(&issuers)
	if err != nil {
		return false, nil, err
	}
	if len(issuers) == 0 {
		return false, nil, err
	}
	for _, issuer := range issuers {
		if issuer.Status == constant.IssuerStatusOk {
			validDid = append(validDid, issuer.Did)
		}
	}
	return true, validDid, nil
}

// 获取用户did对应的有效发证方did
func getValidIssuerDid(engine *xorm.Engine, dids []string, queryType bool) (validDid []string, err error) {
	condition := ""
	args := make([]interface{}, 0)

	if queryType {
		//查询用户有效的凭证，获取发证方did
		combineSql("status != ? ", &condition, constant.CredentialStatusInvalid, &args)
	} else {
		//已被吊销的凭证
		combineSql("status = ? ", &condition, constant.CredentialStatusInvalid, &args)
	}

	combineSql(" and (owner_did = ? ", &condition, dids[0], &args)
	if len(dids) > 1 {
		for _, i := range dids[1:] {
			combineSql(" or owner_did = ? ", &condition, i, &args)
		}
	}
	combineSql(")", &condition, "", nil)

	creds := make([]*entity.VerifiableCredential, 0)
	err = engine.Where(condition, args...).Find(&creds)
	if err != nil {
		return nil, err
	}
	if len(creds) == 0 {
		return
	}
	//获取有效的发证方did
	condition = ""
	args = make([]interface{}, 0)

	combineSql("status = ? ", &condition, constant.IssuerStatusOk, &args)
	combineSql(" and (did = ? ", &condition, creds[0].IssuerDid, &args)
	if len(creds) > 1 {
		for _, i := range creds[1:] {
			combineSql(" or did = ? ", &condition, i.IssuerDid, &args)
		}
	}
	combineSql(")", &condition, "", nil)

	issuers := make([]*entity.AuthorityIssuer, 0)
	err = engine.Where(condition, args...).Find(&issuers)
	if err != nil {
		return nil, err
	}
	if len(issuers) == 0 {
		return
	}
	//返回有效的发证方did
	for _, issuer := range issuers {
		validDid = append(validDid, issuer.Did)
	}
	return
}

func getJoinSession(engine *xorm.Engine) *xorm.Session {
	return engine.Table("verifiable_credential").Alias("vc").
		Join("LEFT", []string{"credential_cpt", "cc"}, "cc.id = vc.cpt_id").
		Join("LEFT", []string{"authority_issuer", "ai"}, "ai.did = vc.issuer_did").
		Join("LEFT", []string{"revoked_credential", "rc"}, "rc.credential_id = vc.id")
}

func getJoinSession2(engine *xorm.Engine) *xorm.Session {
	return engine.Table("verifiable_credential").Alias("vc").
		Join("LEFT", []string{"credential_cpt", "cc"}, "cc.id = vc.cpt_id")
}
